#ifndef _RTOBJECT_H_
#define _RTOBJECT_H_

#include <QObject>
#include <QString>
#include <QDateTime>
#include <QList>
#include <QSet>
#include <QScriptable>
#include <QMetaType>

#include "RtEvent.h"

#define ERROR_QUEUE_DEPTH 5

namespace H5
{
class H5File;
class Group;
}

class RtRoot;
class RtObject;


typedef QList<RtObject*> RtObjectList;

/** Base class of all RtLab objects.

\ingroup RtCore

*/
class RTLAB_BASE_EXPORT RtObject : public QObject, protected QScriptable
{
	Q_OBJECT
	Q_PROPERTY(QString whatsThis READ whatsThis)
	Q_PROPERTY(QString lastError READ lastError)

protected:
	QString whatsThis_;

public:
	const QString& whatsThis() const { return whatsThis_; }

protected:
    /// Queue of last errors
    QList<RtErrorEntry> error_queue;
	/// Push an error in the error queue
    void pushError(const QString& type, const QString& descr = QString());
    /// Get the last error that occured in this object
    QString lastError() const
    {
        return error_queue.isEmpty() ? QString() : error_queue.first().toString();
    }

	/// Create a script object of obj in this RtObject's script engine
	void createScriptObject(RtObject* obj) const;
	/// Throw a script error with message msg
	void throwScriptError(const QString& msg) const;
	/** Check if name is a legal name for an RtObject.
	Names should start with a letter and contain letters, numbers or the underscore _.
	This function also checks if there are any sibbling objects with the same name.
	*/
	bool checkName(const QString& name) const;

public:
    /** Write the contents of the RtObject to a HDF5 file.
      In this base class implementation, properties are written as attributes.
      */
    virtual void writeH5(H5::H5File* file, RtObject* from, bool recursive = true);
    virtual bool readH5(H5::Group *file);

protected:
    /// A collection of "display widgets" that recieve notification messages.
	RtEventSinkMap widgets;
    /// Notify my "widgets" to update their display.
	void updateWidgets() 
	{
		widgets.postUpdate(this);
	}

public:
    /// Add a display widget
	void add_widget(QObject* o, RtUpdateFlag* f) { widgets.add(o,f); }
    /// Remove a display widget
    void remove_widget(QObject* o) { widgets.remove(o); }


    bool event(QEvent* e)
	{
        if (RtEvent::isRtEvent(e)) { rtEvent((RtEvent*)e); return true; }
        else return QObject::event(e);
	}

protected:
    /// Handle RtLab events
    virtual void rtEvent(RtEvent* e);
    // for handling children deletions
    virtual void childEvent ( QChildEvent * event );

public:
    /// A critical section for synching thread access to this object
    critical_section comm_lock;

protected:
    static RtRoot root_;

public:
    /// Obtain a pointer to the RtRoot object.
    static RtRoot* root() { return &root_; }

protected:
    // this is a duplicate to QtObjects's list
    // so that we can handle child ordering stuff (insertBefore etc.)
    RtObjectList children_;

public:
    Q_INVOKABLE
    RtObject(const QString& name, const QString& descr = QString());
	virtual ~RtObject(void);

	//virtual void attach();
	virtual void detach();

	void objectTree(QString& S, int level) const;

	/// Register this RtObject's types with the script engine.
	virtual void registerTypes(QScriptEngine* eng) { Q_UNUSED(eng); }

	/// Get the objects full name, e.g., dev.ifc1.obj1
	QString fullName() const;
	static RtObject* findByName(const QString& name);
	static QList<RtObject*> findByWildcard(const QString& wildcard, RtObject* from = 0);

public slots:
	/// Print a backtrace of recent errors
	QString errorBacktrace() const;
	/// Print a string representation of the object
	QString toString() const { return fullName(); }
	/// Print the objects children hierarchy
	QString objectTree() const
	{
		QString S;
		int ind = 0;
		objectTree(S,ind);
		return S;
	}
	/// List the objects properties
	QString listProperties() const;
	/// List the objects scriptable functions
	QString listFunctions() const;

    // Core DOM API 1. implementation
    /// Return the object's parent
    RtObject* parent() const { return qobject_cast<RtObject*>(QObject::parent()); }
    /// Return a list of children of this object
    RtObjectList children() const { return children_; }
    /// Returns true if the object has children
    bool hasChildren() const { return !children_.isEmpty(); }
    /// Adds a new child RtObject, as the last child.
    void appendChild(RtObject* obj);
    /// Adds a new child RtObject, as the last child.
    void insertBefore(RtObject* newobj, RtObject* existingobj);
    /// Clone a RtObject with its child objects.
    RtObject* clone();
    /// Remove a child and return a pointer to it.
    RtObject* removeChild(RtObject* obj);
    /// Replace a child and return a pointer to the old child.
    RtObject* replaceChild(RtObject* newobj, RtObject* oldobj);

signals:
	/// Fired when object is deleted
	void objectDeleted(RtObject* obj);
	/// Fired when object is created
	void objectCreated(RtObject* obj);
	/// Fired when properties have changed
	void propertiesChanged();
};

Q_DECLARE_METATYPE(RtObject*)
Q_DECLARE_METATYPE(RtObjectList)

int registerRtObjectStar(QScriptEngine* eng);

#endif
